package org.budo.netty.servlet.server.handler.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;

import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;

import org.budo.netty.servlet.server.event.EventBus;
import org.budo.netty.servlet.server.handler.BudoServletHandler;
import org.budo.netty.servlet.server.handler.servlet.dispatcher.BudoPathRequestDispatcher;
import org.budo.netty.servlet.server.session.BudoHttpSession;
import org.budo.netty.servlet.server.session.repository.SessionRepository;
import org.budo.netty.servlet.util.NumberUtil;
import org.budo.netty.servlet.util.RequestUtil;

/**
 * @author li
 */
public abstract class AbstractBudoServletRequest implements BudoServletRequest {
    private static final String HEADER_CONTENT_LENGTH = "Content-Length";

    private static final String HEADER_HOST = "Host";

    private BudoServletHandler budoServletHandler;

    private Hashtable<String, Object> attributes = new Hashtable<String, Object>();

    private String characterEncoding;

    private HttpSession session;

    public BudoServletHandler getBudoServletHandler() {
        return this.budoServletHandler;
    }

    @Override
    public String getServerName() {
        return this.getHeader(HEADER_HOST);
    }

    @Override
    public void removeAttribute(String name) {
        EventBus eventBus = ((BudoServletContext) this.getServletContext()).getBudoServletServer().getEventBus();
        eventBus.requestAttributeRemove(this, name);

        this.attributes.remove(name);
    }

    @Override
    public void setAttribute(String name, Object value) {
        EventBus eventBus = ((BudoServletContext) this.getServletContext()).getBudoServletServer().getEventBus();
        eventBus.requestAttributeSetted(this, name, value);
        this.attributes.put(name, value);
    }

    public void setBudoServletHandler(BudoServletHandler budoServletHandler) {
        this.budoServletHandler = budoServletHandler;
    }

    @Override
    public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException {
        this.characterEncoding = characterEncoding;
    }

    @Override
    public String getRequestedSessionId() {
        Cookie[] cookies = this.getCookies();
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(BUDO_SESSION_ID)) {
                return cookie.getValue();
            }
        }

        return null;
    }

    @Override
    public HttpSession getSession() {
        return this.getSession(true);
    }

    @Override
    public HttpSession getSession(boolean create) {
        if (null != this.session) {
            return this.session;
        }

        SessionRepository sessionRepository = this.getBudoServletHandler().getBudoServletServer().getSessionRepository();
        String sessionId = this.getRequestedSessionId();
        HttpSession session = sessionRepository.findById(sessionId); // 有 sessionId 就查询

        // 匹配到旧的 session
        if (null != session) {
            ((BudoHttpSession) session).setIsNew(false);
            this.session = session; // 赋值
            return this.session;
        }

        // 未匹配到旧Session
        if (!create) {
            return null; // 不创建，返回空
        }

        // 如有必要，生成sessionId
        if (null == sessionId) {
            sessionId = sessionRepository.createSessionId();
        }

        // new session
        session = new BudoHttpSession(sessionId, this.getServletContext());
        ((BudoHttpSession) session).setIsNew(true);
        sessionRepository.saveSession(session);

        EventBus eventBus = this.getBudoServletHandler().getBudoServletServer().getEventBus();
        eventBus.sessionCreated(session);

        return this.session = session; // 赋值
    }

    @Override
    public int getIntHeader(String name) {
        String value = this.getHeader(name);

        if (null == value) {
            return -1;
        }

        return NumberUtil.toInt(value);
    }

    @Override
    public Object getAttribute(String name) {
        return this.attributes.get(name);
    }

    @Override
    public Enumeration<String> getAttributeNames() {
        return this.attributes.keys();
    }

    @Override
    public String getContentType() {
        return this.getHeader("Content-Type");
    }

    @Override
    public String getCharacterEncoding() {
        if (this.characterEncoding != null) {
            return this.characterEncoding;
        }

        return RequestUtil.getCharacterEncodingFromContentType(this);
    }

    @Override
    public int getContentLength() {
        return this.getIntHeader(HEADER_CONTENT_LENGTH);
    }

    @Override
    public String getContextPath() {
        return this.getServletContext().getContextPath();
    }

    @Override
    public String getParameter(String name) {
        String[] values = this.getParameterValues(name);
        return (null == values) ? null : values[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = this.getParameterMap();
        return parameterMap.get(name);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    @Override
    public RequestDispatcher getRequestDispatcher(String path) {
        return new BudoPathRequestDispatcher(path);
    }

    // getServletPath是返回的是项目名到当前jsp文件的路径（意思就是在这个项目首页到文件的路径）
    // getRequestURI是返回的是项目名到整个文件的请求路径
    // getRequestURL是返回的整个URL的路径请求（意思就是返回的浏览器地址栏的整个地址）
    /**
     * 不包含 queryString
     */
    @Override
    public StringBuffer getRequestURL() {
        return new StringBuffer(this.getScheme() + "://" + this.getServerName() + this.getRequestURI());
    }

    @Override
    public Principal getUserPrincipal() {
        return null;
    }

    @Override
    public ServletContext getServletContext() {
        return this.getBudoServletHandler().getBudoServletServer().getServletContext();
    }

    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(this.getParameterMap().keySet());
    }

    @Override
    public Part getPart(String s) throws IOException, ServletException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Collection<Part> getParts() throws IOException, ServletException {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getPathTranslated() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getRealPath(String path) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getRemoteHost() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getRemoteUser() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isAsyncStarted() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isAsyncSupported() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isRequestedSessionIdFromCookie() {
        throw new UnsupportedOperationException();
    }

    /**
     * @deprecated
     */
    @Override
    public boolean isRequestedSessionIdFromUrl() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isRequestedSessionIdFromURL() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isRequestedSessionIdValid() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isSecure() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean isUserInRole(String role) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void login(String username, String password) throws ServletException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void logout() throws ServletException {
        throw new UnsupportedOperationException();
    }

    /**
     * javax.servlet.AsyncListener - 如果在添加了侦听器的ServletRequest上启动的异步操作已完成，超时或导致错误，将会通知侦听器。
     */
    @Override
    public AsyncContext startAsync() throws IllegalStateException {
        throw new UnsupportedOperationException();
    }

    @Override
    public long getDateHeader(String name) {
        throw new UnsupportedOperationException();
    }

    @Override
    public DispatcherType getDispatcherType() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean authenticate(HttpServletResponse httpServletResponse) throws IOException, ServletException {
        throw new UnsupportedOperationException();
    }

    @Override
    public String changeSessionId() {
        throw new UnsupportedOperationException();
    }

    @Override
    public AsyncContext getAsyncContext() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getAuthType() {
        throw new UnsupportedOperationException();
    }

    @Override
    public long getContentLengthLong() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getLocalAddr() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Enumeration<Locale> getLocales() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getLocalPort() {
        throw new UnsupportedOperationException();
    }

    @Override
    public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
        throw new UnsupportedOperationException();
    }

    @Override
    public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) throws IOException, ServletException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Locale getLocale() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getPathInfo() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getServletPath() {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setServletResolved(ServletResolved servletResolved) {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getQueryString() {
        throw new UnsupportedOperationException();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int getRemotePort() {
        throw new UnsupportedOperationException();
    }
}